import logging
import tqdm
from utils.calculate_utils import jaccard_similarity


def get_top_level_node_no_change_token(G):
    if G.number_of_nodes() == 0:
        return []
    # 找到虚拟根节点
    virtual_root = None
    for node in G.nodes:
        if G.nodes[node]["kind"] == 15:
            virtual_root = node
            break

    if virtual_root is None:
        # 如果没有虚拟根节点，返回空列表
        return []

    # 找到虚拟根节点的所有子节点
    top_level_nodes = list(G.successors(virtual_root))

    # 获取这些子节点的 nodeText
    node_no_change_token= []
    for node in top_level_nodes:
        diff_no_change_token = G.nodes[node].get("diffnochangeToken", [])
        node_no_change_token.extend(diff_no_change_token)

    return node_no_change_token


def diff_similarity_stage(top_candidates, same_ast_similarity_threshold):
    """
    计算异构语义相似度
    参数:
        top_candidates: 计算同构比例后的候选函数
        same_ast_similarity_threshold: 同构比例阈值
    返回:
        sorted_candidates: 按距离排序后的候选函数
        best_match: 最佳匹配
    """

    best_distance = float('inf')
    best_match = None

    with tqdm as pbar:
        for i, match in enumerate(top_candidates):
            npm_id = match['npm_id']

            try:
                # 获取已保存的剪枝后的图
                p1 = match['P1']
                p2 = match['P2']

                # 剪枝后的AST的根节点的文本内容
                arkts_top_level_node_no_change_token = get_top_level_node_no_change_token(p1)
                npm_top_level_node_no_change_token = get_top_level_node_no_change_token(p2)

                # 计算异构的语义相似度
                diff_similarity = jaccard_similarity(set(arkts_top_level_node_no_change_token),
                                                     set(npm_top_level_node_no_change_token))

                # 基于距离的指标计算
                distance = abs(same_ast_similarity_threshold - match['isomorphic_ratio']) + abs(1 - diff_similarity)

                # 更新结果
                top_candidates[i]['diff_similarity'] = diff_similarity
                top_candidates[i]['distance'] = distance

                # 删除不再需要的图数据，减少内存占用
                if 'P1' in top_candidates[i]:
                    del top_candidates[i]['P1']

                if 'P2' in top_candidates[i]:
                    del top_candidates[i]['P2']

                # 更新最佳匹配
                if distance < best_distance:
                    best_distance = distance
                    best_match = top_candidates[i]

                # 更新进度条
                pbar.set_postfix(
                    当前距离=f"{distance:.4f}",
                    最佳距离=f"{best_distance:.4f}"
                )

            except Exception as e:
                logging.info(f"计算异构相似度时出错 (NPM ID: {npm_id}): {e}")
                # 确保删除图数据
                if 'P1' in top_candidates[i]:
                    del top_candidates[i]['P1']
                if 'P2' in top_candidates[i]:
                    del top_candidates[i]['P2']

            pbar.update(1)

    # 按距离排序结果
    sorted_candidates = sorted(top_candidates, key=lambda x: x['distance'] if x['distance'] != -1 else float('inf'))

    return sorted_candidates, best_match
